package net.luohuasheng.bee.proxy.mybatis.common.utils;

import net.luohuasheng.bee.jdbc.tools.common.utils.ReflectionUtils;
import net.luohuasheng.bee.proxy.mybatis.common.enums.BasicMethod;
import net.luohuasheng.bee.proxy.mybatis.dao.BasicReadDao;
import net.luohuasheng.bee.proxy.mybatis.executor.processor.BaseProcessor;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.jdbc.JdbcTransaction;

import javax.sql.DataSource;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

public class CrudUtils {
    private static Map<String, BaseProcessor> baseProcessorMap = new HashMap<>(0);

    public static BaseProcessor getBaseProcessor(Configuration configuration, String id) {
        String name = id.substring(id.lastIndexOf(".") + 1);
        BasicMethod method = BasicMethod.getMethodByCode(name);
        if (method == null) {
            return null;
        }
        String className = id.substring(0, id.lastIndexOf("."));
        try {
            Class<?> classZ = CrudUtils.class.getClassLoader().loadClass(className);
            Type[] genericInterfaces = classZ.getGenericInterfaces();
            ParameterizedType genericInterface = (ParameterizedType) genericInterfaces[0];
            if (BasicReadDao.class.isAssignableFrom((Class<?>) genericInterface.getRawType())) {
                Class<?> entityClass = (Class<?>) genericInterface.getActualTypeArguments()[0];
                return baseProcessorMap.computeIfAbsent(id, v -> createProcessor(configuration, method.getProcessorClass(), entityClass));
            }
        } catch (ClassNotFoundException ex) {
            ex.printStackTrace();
        }
        return null;
    }

    private static BaseProcessor createProcessor(Configuration configuration, Class<? extends BaseProcessor> processorClass, Class<?> entityClass) {
        try {
            Constructor<? extends BaseProcessor> constructor = processorClass.getConstructor(Configuration.class, Class.class);
            return constructor.newInstance(configuration, entityClass);
        } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static DataSource dataSource(Invocation invocation) {
        Transaction transaction = (Transaction) ReflectionUtils.invokeMethod(invocation.getTarget(), "getTransaction");
        return (DataSource) ReflectionUtils.getFieldValue(transaction, "dataSource");
    }
}
